/*
 *              Copyright (C) 2011 The MusicMod Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *            http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.musicmod.android.receiver;

import org.musicmod.android.Constants;
import org.musicmod.android.MusicPlaybackService;
import org.musicmod.android.R;
import org.musicmod.android.activity.MusicBrowserActivity;
import org.musicmod.android.activity.MusicPlaybackActivity;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Environment;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.RemoteViews;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.PorterDuff.Mode;
import android.graphics.RectF;
import android.graphics.Rect;

/**
 * Simple widget to show currently playing album art along with play/pause and
 * next track buttons.
 */
public class MusicAppWidgetProvider2x2 extends AppWidgetProvider implements
		Constants {

	private static MusicAppWidgetProvider2x2 sInstance;
	CharSequence mTrackName;
	CharSequence mArtistName;
	long mAlbumId;
	long mSongId;
	Bitmap mAlbumArt;
	CharSequence mErrorState;

	public static synchronized MusicAppWidgetProvider2x2 getInstance() {
		if (sInstance == null) {
			sInstance = new MusicAppWidgetProvider2x2();
		}
		return sInstance;
	}

	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager,
			int[] appWidgetIds) {
		defaultAppWidget(context, appWidgetIds);

		// Send broadcast intent to any running MusicPlaybackService so it can
		// wrap around with an immediate update.
		Intent updateIntent = new Intent(SERVICECMD);
		updateIntent.putExtra(CMDNAME, CMDMUSICWIDGETUPDATE_2x2);
		updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,
				appWidgetIds);
		updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
		context.sendBroadcast(updateIntent);
	}

	private Bitmap processImage(Bitmap bitmap, Context context, int width,
			int height) {

		DisplayMetrics dm = new DisplayMetrics();
		dm = context.getResources().getDisplayMetrics();

		int max_width_height = 160;

		int desired_width;
		int desired_height;

		float density = dm.density;
		float ratio = (float) width / (float) height;

		if (ratio > 1.0) {
			desired_width = (int) (max_width_height * density);
			desired_height = (int) (max_width_height / ratio * density);
		} else if (ratio < 1.0) {
			desired_width = (int) (max_width_height / ratio * density);
			desired_height = (int) (max_width_height * density);
		} else {
			desired_width = (int) (max_width_height * density);
			desired_height = (int) (max_width_height * density);
		}

		Bitmap resized = Bitmap.createScaledBitmap(bitmap, desired_width,
				desired_height, false);

		Bitmap output = Bitmap.createBitmap(resized.getWidth(),
				resized.getHeight(), Config.ARGB_8888);
		Canvas canvas = new Canvas(output);

		final int color = 0xff424242;
		final Paint paint = new Paint();
		final Rect rect = new Rect(0, 0, resized.getWidth(),
				resized.getHeight());
		final RectF rectF = new RectF(rect);
		final float roundPx = 12;

		paint.setAntiAlias(true);
		canvas.drawARGB(0, 0, 0, 0);
		paint.setColor(color);
		canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

		paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
		canvas.drawBitmap(resized, rect, rect, paint);

		return output;
	}

	/**
	 * Initialize given widgets to default state, where we launch Music on
	 * default click and hide actions if service not running.
	 */
	private void defaultAppWidget(Context context, int[] appWidgetIds) {
		final Resources res = context.getResources();
		final RemoteViews views = new RemoteViews(context.getPackageName(),
				R.layout.music_widget_2x2);

		views.setViewVisibility(R.id.title, View.GONE);
		views.setTextViewText(R.id.artist, res.getText(R.string.emptyplaylist));
		views.setImageViewResource(R.id.albumart,
				R.drawable.albumart_mp_unknown);

		linkButtons(context, views, false /* not playing */);
		pushUpdate(context, appWidgetIds, views);
	}

	private void pushUpdate(Context context, int[] appWidgetIds,
			RemoteViews views) {
		// Update specific list of appWidgetIds if given, otherwise default to
		// all
		final AppWidgetManager gm = AppWidgetManager.getInstance(context);
		if (appWidgetIds != null) {
			gm.updateAppWidget(appWidgetIds, views);
		} else {
			gm.updateAppWidget(new ComponentName(context, this.getClass()),
					views);
		}
	}

	/**
	 * Check against {@link AppWidgetManager} if there are any instances of this
	 * widget.
	 */
	private boolean hasInstances(Context context) {
		AppWidgetManager appWidgetManager = AppWidgetManager
				.getInstance(context);
		int[] appWidgetIds = appWidgetManager
				.getAppWidgetIds(new ComponentName(context, this.getClass()));
		return (appWidgetIds.length > 0);
	}

	/**
	 * Handle a change notification coming over from
	 * {@link MusicPlaybackService}
	 */
	public void notifyChange(MusicPlaybackService service, String what) {
		if (hasInstances(service)) {
			if (BROADCAST_PLAYBACK_COMPLETE.equals(what)
					|| BROADCAST_META_CHANGED.equals(what)
					|| BROADCAST_PLAYSTATE_CHANGED.equals(what)) {
				performUpdate(service, null);
			}
		}
	}

	/**
	 * Update all active widget instances by pushing changes
	 */
	public void performUpdate(MusicPlaybackService service, int[] appWidgetIds) {
		final Resources res = service.getResources();
		final RemoteViews views = new RemoteViews(service.getPackageName(),
				R.layout.music_widget_2x2);

		mTrackName = service.getTrackName();
		mArtistName = service.getArtistName();
		mAlbumId = service.getAlbumId();
		mSongId = service.getAudioId();

		mAlbumArt = processImage(service.getAlbumArt(), service, service
				.getAlbumArt().getWidth(), service.getAlbumArt().getHeight());

		mErrorState = null;

		// Format title string with track number, or show SD card message
		String status = Environment.getExternalStorageState();
		if (status.equals(Environment.MEDIA_SHARED)
				|| status.equals(Environment.MEDIA_UNMOUNTED)) {
			mErrorState = res.getText(R.string.sdcard_busy);
		} else if (status.equals(Environment.MEDIA_REMOVED)) {
			mErrorState = res.getText(R.string.sdcard_missing);
		} else if (mTrackName == null) {
			mErrorState = res.getText(R.string.emptyplaylist);
		}

		if (mErrorState != null) {
			// Show error state to user
			views.setViewVisibility(R.id.title, View.GONE);
			views.setTextViewText(R.id.artist, mErrorState);
			views.setImageViewResource(R.id.albumart,
					R.drawable.albumart_mp_unknown);
		} else {
			// No error, so show normal titles
			views.setViewVisibility(R.id.title, View.VISIBLE);
			views.setTextViewText(R.id.title, mTrackName);
			views.setTextViewText(R.id.artist, mArtistName);
			if (mAlbumArt == null) {
				views.setImageViewResource(R.id.albumart,
						R.drawable.albumart_mp_unknown);
			} else {
				views.setImageViewBitmap(R.id.albumart, mAlbumArt);
			}
		}

		// Set correct drawable for pause state
		final boolean playing = service.isPlaying();
		if (playing) {
			views.setImageViewResource(R.id.control_play,
					R.drawable.widget_pause);
		} else {
			views.setImageViewResource(R.id.control_play,
					R.drawable.widget_play);
		}

		// Link actions buttons to intents
		linkButtons(service, views, playing);

		pushUpdate(service, appWidgetIds, views);
	}

	/**
	 * Link up various button actions using {@link PendingIntents}.
	 * 
	 * @param playerActive
	 *            True if player is active in background, which means widget
	 *            click will launch {@link MusicPlaybackActivity}, otherwise we
	 *            launch {@link MusicBrowserActivity}.
	 */
	private void linkButtons(Context context, RemoteViews views,
			boolean playerActive) {
		// Connect up various buttons and touch events
		Intent intent;
		PendingIntent pendingIntent;

		final ComponentName serviceName = new ComponentName(context,
				MusicPlaybackService.class);

		if (playerActive) {
			intent = new Intent();
			intent.setAction(INTENT_PLAYBACK_VIEWER);
			pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
			views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent);
		} else {
			intent = new Intent(context, MusicBrowserActivity.class);
			pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
			views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent);
		}

		intent = new Intent(TOGGLEPAUSE_ACTION);
		intent.setComponent(serviceName);
		pendingIntent = PendingIntent.getService(context, 0, intent, 0);
		views.setOnClickPendingIntent(R.id.control_play, pendingIntent);

		intent = new Intent(NEXT_ACTION);
		intent.setComponent(serviceName);
		pendingIntent = PendingIntent.getService(context, 0, intent, 0);
		views.setOnClickPendingIntent(R.id.control_next, pendingIntent);

		intent = new Intent(PREVIOUS_ACTION);
		intent.setComponent(serviceName);
		pendingIntent = PendingIntent.getService(context, 0, intent, 0);
		views.setOnClickPendingIntent(R.id.control_prev, pendingIntent);
	}
}
